﻿using System.Collections.Generic;
using System.Html;
using System;

namespace Db7
{
    public class VScrollbar : VirtualElement
    {
        public VScrollbar()
        {
            Tag = "div";

            Init = delegate()
            {
                DefaultPreRender();
            };

            InnerDiv = new VirtualElement();
            InnerDiv.Tag = "div";
            
            
        }

        #region private fields
        private double HeightOrWidthMultiplicationFactor;
        private int BrowserSpecificMaxValueWithoutScaling;
        private bool IgnoreScrollEvent;
        private bool IsMaxedOut;
        private double BrowserSpecificIncrement;
        private int Offset;
        private int PreviousValue;
        private double PreviousScrollTop;
        #endregion

        #region public fields
        public int MaxValue;
        public int? FinalRemainder;
        public bool HorizontalOrientation;
        #endregion

        #region public properties

        

        public IntBinder ScrollValue_DB;

        public int ScrollValue
        {
            get { return IntBinder.Get(ScrollValue_DB); }
            set { 
                ScrollValue_DB = IntBinder.Set(ScrollValue_DB, value);
            }
        }

        private bool ScrollEventInProgress = false;
        private void UpdateScrollbar()
        {
            if (ScrollEventInProgress) return;
            Element e = this.PhysicalElement;
            double scrollTop = this.ScrollValue * this.HeightOrWidthMultiplicationFactor;
            if (e != null)
            {
                string attrib = HorizontalOrientation ? "scrollLeft" : "scrollTop";
                Util.SetAttribute(e, attrib, scrollTop.ToString());
            }
        } 

        public VirtualElement InnerDiv;

        #endregion

        public void DefaultPreRender()
        {
            //TODO:  any type of cleanup?
            if (ScrollValue_DB != null)
            {
                ScrollValue_DB.AddOnUpdateNotification(delegate() { this.UpdateScrollbar(); });
            }
            Children = new List<VirtualElement>();
            Children.Add(this.InnerDiv);
            Navigator nav = Window.Navigator;
            double? rawHeightOrWidth = Util.NaN;
            if (this.Dimensions != null)
            {
                if (HorizontalOrientation)
                {
                    rawHeightOrWidth = this.Dimensions.Width == Util.NaN ? (this.Dimensions.Width_D == Util.NaN ? Util.NaN : this.Dimensions.Width_D) : this.Dimensions.Width;
                }
                else
                {
                    rawHeightOrWidth = this.Dimensions.Height == Util.NaN ? (this.Dimensions.Height_D == Util.NaN ? Util.NaN : this.Dimensions.Height_D) : this.Dimensions.Height;
                }
            }
            switch (nav.AppName)
            {
                case IE:
                    this.BrowserSpecificMaxValueWithoutScaling = 25000;
                    this.BrowserSpecificIncrement = 50;
                    if (HorizontalOrientation)
                    {
                        this.HeightOrWidthMultiplicationFactor = (rawHeightOrWidth == Util.NaN ? 20 : (double)rawHeightOrWidth / 32.4);
                    }
                    else
                    {
                        this.HeightOrWidthMultiplicationFactor = (rawHeightOrWidth == Util.NaN ? 20 : (double)rawHeightOrWidth / 8.5);
                    }
                    break;
                case Firefox:
                    this.BrowserSpecificMaxValueWithoutScaling = 900000;
                    this.BrowserSpecificIncrement = 19;
                    this.HeightOrWidthMultiplicationFactor = (rawHeightOrWidth == Util.NaN ? 20 : (double)rawHeightOrWidth / 22);
                    break;
                default:
                    throw new Exception("this browser is not supported");
            }

            #region Inner Div
            int numberOfRecordsToLeaveForLastPage = 0;
            if (this.FinalRemainder != null)
            {
                numberOfRecordsToLeaveForLastPage = (int)this.FinalRemainder;
            }

            //int innerDivHeightOrWidth = (int)(((int)this.MaxValue - numberOfRecordsToLeaveForLastPage) * this.HeightOrWidthMultiplicationFactor + this.Dimensions.Height);
            double pre_innerDivHeightOrWidth = ((int)this.MaxValue - numberOfRecordsToLeaveForLastPage) * this.HeightOrWidthMultiplicationFactor;
            int innerDivHeightOrWidth = HorizontalOrientation ?
                (int)(pre_innerDivHeightOrWidth + this.Dimensions.Width)
                : (int)(pre_innerDivHeightOrWidth + this.Dimensions.Height)
            ;
            if (this.MaxValue > this.BrowserSpecificMaxValueWithoutScaling)
            {
                this.IsMaxedOut = true;
                innerDivHeightOrWidth = Math.Min(innerDivHeightOrWidth, this.BrowserSpecificMaxValueWithoutScaling);

            }
            //Util.Debug();
            if (HorizontalOrientation)
            {
                InnerDiv.DimensionsNN.Width = innerDivHeightOrWidth;
                DimensionsNN.Height = 18;
                StylesNN["overflow-x"] = "scroll";
                Styles["overflow-y"] = "hidden";
            }
            else
            {
                InnerDiv.DimensionsNN.Height = innerDivHeightOrWidth;
                DimensionsNN.Width = 18;
                StylesNN["overflow-y"] = "scroll";
                Styles["overflow-x"] = "hidden";
            }
            InnerDiv.Text = "&nbsp;";
            RenderContext.RegisterOnMouseMoveHandler(this, OnMouseMove);
            #endregion
            
        }

        private void OnMouseMove(ElementEvent e)
        {
            DivElement element = e.SrcElement.As<DivElement>();
            if(element == null) return;
            element.AddEventListener("scroll", _onScrollEvent, false);
            RenderContext.UnRegisterOnMouseMoveHandler(this);
        }

        private void _onScrollEvent(ElementEvent args)
        {
            //Util.Debug();
            ScrollEventInProgress = true;
            string attrib = HorizontalOrientation ? "scrollLeft" : "scrollTop";
            DivElement element = args.SrcElement.As<DivElement>();
            object scrollTopAttrib = Util.GetAttribute(element, attrib);
            double scrollTop = double.Parse( scrollTopAttrib.ToString());// .GetProperty(Element.AttributeNames.scrollTop);
            //Util.Debug();
            if (!this.IsMaxedOut)
            {
                ScrollValue = (int)Math.Floor(scrollTop / this.HeightOrWidthMultiplicationFactor);
                ScrollEventInProgress = false;
                return;
            }
            #region Maxed Out
            if (Math.Abs(scrollTop - this.PreviousScrollTop) < this.BrowserSpecificIncrement)
            {
                ScrollValue = this.PreviousValue;
                ScrollEventInProgress = false;
                return;
            }
            #region change larger than 50
            
            if (scrollTop - this.PreviousScrollTop == this.BrowserSpecificIncrement)
            {
                #region Went Down by One
                if (this.PreviousScrollTop == 0d)
                {
                    this.Offset = 1 - (int)this.BrowserSpecificIncrement;
                    this.PreviousValue = 1;
                    this.PreviousScrollTop = scrollTop;
                    ScrollValue = this.PreviousValue;
                    ScrollEventInProgress = false;
                    return;
                }
                #region Not at the top
                if (this.Offset >= 0)
                {
                    #region Let the scrolling happen
                    this.PreviousScrollTop = scrollTop;
                    this.Offset = 1 - (int)this.BrowserSpecificIncrement;
                    this.PreviousValue++;
                    ScrollValue = this.PreviousValue;
                    ScrollEventInProgress = false;
                    return;
                    #endregion
                }
                #region Keep Scrollbar where it is
                this.Offset++;
                this.PreviousValue++;
                this.IgnoreScrollEvent = true;
                scrollTop = this.PreviousScrollTop;
                Util.SetAttribute(element, attrib, scrollTop.ToString());
                ScrollEventInProgress = false;
                return;
                #endregion
                #endregion
                #endregion
            }
            else if (scrollTop - this.PreviousScrollTop == -this.BrowserSpecificIncrement)
            {
                #region Went Up By One
                if (this.PreviousScrollTop == 24600d)
                {
                    this.Offset = (int)this.BrowserSpecificIncrement - 1;
                    this.PreviousValue = this.MaxValue - 1;
                    this.PreviousScrollTop = scrollTop;
                    ScrollValue = this.PreviousValue;
                    ScrollEventInProgress = false;
                    return;
                }
                #region Not At the bottom
                if (this.Offset <= 0)
                {
                    #region Let the scrolling happen
                    this.PreviousScrollTop = scrollTop;
                    this.Offset = (int)this.BrowserSpecificIncrement - 1;
                    this.PreviousValue--;
                    ScrollValue = this.PreviousValue;
                    ScrollEventInProgress = false;
                    return;
                    #endregion
                }
                #region Keep Scrollbar where it is
                this.Offset--;
                this.PreviousValue--;
                this.IgnoreScrollEvent = true;
                scrollTop = this.PreviousScrollTop;
                Util.SetAttribute(element, attrib, scrollTop.ToString());
                ScrollEventInProgress = false;
                return;
                #endregion
                #endregion
                #endregion

            }
            int ubound; int lbound;
            if (HorizontalOrientation)
            {
                ubound = (int)Math.Ceil(scrollTop * this.MaxValue / (this.InnerDiv.Dimensions.Width - 400));
                lbound = (int)Math.Floor(scrollTop * this.MaxValue / (this.InnerDiv.Dimensions.Width - 400));
            }
            else
            {
                ubound = (int)Math.Ceil(scrollTop * this.MaxValue / (this.InnerDiv.Dimensions.Height - 400));
                lbound = (int)Math.Floor(scrollTop * this.MaxValue / (this.InnerDiv.Dimensions.Height - 400));
            }
            this.PreviousValue = ubound;
            this.PreviousScrollTop = scrollTop;
            this.Offset = 0;
            return;
            #endregion
            #endregion
        }
    }
}
